package com.execdemo.demoorderservice.redis.impl;

import com.execdemo.demoorderservice.redis.Ilock;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;

import java.util.UUID;
import java.util.concurrent.TimeUnit;


public class myRedisLock implements Ilock {

    private String lockName;
    private StringRedisTemplate stringRedisTemplate;
    private RedisTemplate redisTemplate;

    public myRedisLock(String lockName, StringRedisTemplate stringRedisTemplate, RedisTemplate redisTemplate) {
        this.lockName = lockName;
        this.stringRedisTemplate = stringRedisTemplate;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 构造函数用于初始化一个Redis分布式锁实例
     *
     * @param lockName            锁的名称，通常对应Redis中的键名通过这个名称来标识锁
     * @param stringRedisTemplate Redis模板，用于操作Redis数据库这个模板提供了与Redis交互的方法
     */
    public myRedisLock(String lockName, StringRedisTemplate stringRedisTemplate) {
        this.lockName = lockName;
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 定义锁的前缀常量，用于在缓存或同步机制中标识锁对象
     * 该前缀与键组合使用，以确保锁的唯一性和正确性
     */
    private static final String LOCK_PREFIX = "lock:";
    private static final String ID_PREFIX = UUID.randomUUID().toString() + "-";

    /**
     * 尝试获取锁
     *
     * @param timeOutSec 超时时间（秒）
     * @return 如果成功获取锁则返回true，否则返回false
     */
    @Override
    public boolean tryLock(long timeOutSec) {
        // 获取当前线程的ID
        long threadId = Thread.currentThread().getId();

        // 使用Redis的setIfAbsent方法尝试设置键值对，如果键不存在则设置成功，表示获取锁成功
        // 键为LOCK_PREFIX加上锁的名称，值为当前线程的ID，自动设置过期时间为timeOutSec秒
        Boolean success = stringRedisTemplate.opsForValue()
                .setIfAbsent(LOCK_PREFIX + lockName, threadId + "", timeOutSec, TimeUnit.SECONDS);

        // 判断设置操作是否成功，成功则表示获取锁成功，返回true
        return Boolean.TRUE.equals(success);
    }


    /**
     * 解锁方法
     * <p>
     * 删除之前加锁时设置的锁键，以解除锁的状态
     * 这里利用了Redis的特性，当一个键被删除后，其他任何等待该键的分布式锁的线程都有机会重新获取锁
     * <p>
     * 注意：此方法调用者已经通过某种方式获取了锁，并且锁的标识是通过锁名来区分的
     */
    @Override
    public void unlock() {
        /**
         * 删除指定的锁
         * 此方法用于释放不再需要的锁，以避免资源泄露
         *
         * @param lockName 锁的名称，通常与业务相关，用于唯一标识一个锁
         */

        // 构造当前线程唯一的ID，用于标识获取锁的线程
        String theardId = ID_PREFIX + Thread.currentThread().getId();

        // 从Redis中获取锁对应的值，即持有锁的线程ID
        String lockId = stringRedisTemplate.opsForValue().get(LOCK_PREFIX + lockName);

        // 检查当前线程是否是持有锁的线程
        if (theardId.equals(lockId)){
            // 如果是，則释放锁，即从Redis中删除该锁记录
            stringRedisTemplate.delete(LOCK_PREFIX + lockName);
        }

    }
}
